package cn.tedu.lucky.draw.server.aspect;

import cn.tedu.lucky.draw.server.common.annotation.OperationLog;
import cn.tedu.lucky.draw.server.protocol.po.UserLog;
import cn.tedu.lucky.draw.server.service.IUserLogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

@Slf4j
@Aspect
@Component
public class LogAspect {

    @Autowired
    private IUserLogService userLogService;

    /**
     * 通过@Pointcut注解来指定切点
     */
    @Pointcut("@annotation(cn.tedu.lucky.draw.server.common.annotation.OperationLog)")
    public void logPointCut() {
        // 空方法，只用于承载指定注解的定义
    }

    @Around("logPointCut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        int status = 1;
        String error = "";
        long time = 0;
        System.out.println("save方法执行前记录时间");
        long start = System.currentTimeMillis();
        try {
            Object proceed = joinPoint.proceed();
            long end = System.currentTimeMillis();
            System.out.println("save方法执行后记录时间");
            time = end - start;
            return proceed;
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            System.out.println("save方法执行异常时记录时间");
            time = end - start;
            status = 0;
            error = e.getMessage();
            throw e;
        } finally {
            saveOperationLog(joinPoint, status, time, error);
        }

    }

    private void saveOperationLog(
            ProceedingJoinPoint joinPoint, int status, long time, String error
    ) throws NoSuchMethodException, JsonProcessingException {
        //1.记录用户行为日志
        //1.1 获取用户ip
        //RequestContextHolder是一个工具类，用于访问容器Tomcat
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String ip = request.getRemoteAddr();
        //1.2 获取登录用户
        String username = "admin";
        //1.3 获取操作名
        //1.3.1 获取类的字节码文件
        Class<?> targetCl = joinPoint.getTarget().getClass();
        //1.3.2 获取方法签名对象
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = targetCl.getDeclaredMethod(
                ms.getName(), ms.getParameterTypes()
        );
        //1.3.3 获取方法的注解
        OperationLog annotation = ms.getMethod().getAnnotation(OperationLog.class);
        //1.3.4 获取操作名称
        String operation = annotation.operation();
        //1.4 获取方法名称（类名+方法名）
        String method = targetCl.getName() + "." + targetMethod.getName();

        Object[] args = joinPoint.getArgs();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        //转换数据类型
        String params = objectMapper.writeValueAsString(args);

        //2. 封装用户行为日志
        UserLog userLog = new UserLog();
        userLog.setIp(ip);
        userLog.setUsername(username);
        userLog.setCreateTime(LocalDateTime.now());
        userLog.setOperation(operation);
        userLog.setMethod(method);
        userLog.setParams(params);
        userLog.setStatus(status);
        userLog.setError(error);
        userLog.setTime(time);

        log.info("用户行为日志：{}", userLog);
        userLogService.save(userLog);
    }

}
